package cn.itcast.hotel.service.impl;

import cn.itcast.hotel.mapper.HotelMapper;
import cn.itcast.hotel.pojo.Hotel;
import cn.itcast.hotel.pojo.HotelDoc;
import cn.itcast.hotel.pojo.RequestParams;
import cn.itcast.hotel.pojo.PageResult;
import cn.itcast.hotel.service.IHotelService;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Service
public class HotelService extends ServiceImpl<HotelMapper, Hotel> implements IHotelService {

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Override
    public PageResult search(RequestParams params) throws IOException {
        //key "如家"，page：1，size：5
        SearchRequest searchRequest = new SearchRequest("hotel");

        buildBasicQuery(params,searchRequest);

        //排序
        //searchRequest.source().sort("price", SortOrder.ASC);
        if (StringUtils.isNotBlank(params.getLocation())){
            searchRequest.source().sort(SortBuilders.geoDistanceSort("location",new GeoPoint(params.getLocation()))
                                        .unit(DistanceUnit.KILOMETERS)
                                        .order(SortOrder.ASC));
        }
        //分页
        Integer page = params.getPage(); //当前页码
        Integer size = params.getSize();//每页显示条数
        searchRequest.source().from((page-1)*size);  //查询第一页   (当前页码-1)*每页显示条数
        searchRequest.source().size(size);

        //高亮查询
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("name");
        highlightBuilder.preTags("<em  style='color:red'>");
        highlightBuilder.postTags("</em>");
        highlightBuilder.requireFieldMatch(false);    //这句话加不加取决于   查询的域名和高亮的域名一致不,一样可以不加
        searchRequest.source().highlighter(highlightBuilder);

        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        return handlerResponse(searchResponse);
    }



    private void buildBasicQuery(RequestParams requestParams, SearchRequest searchRequest) {
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();  //复合查询
        if (StringUtils.isNotBlank(requestParams.getKey())) {
            boolQueryBuilder.must(QueryBuilders.matchQuery("all", requestParams.getKey()));  //根据关键字查询
        } else {
           boolQueryBuilder.must(QueryBuilders.matchAllQuery());  //查询所有
        }
        //如果city不为空
        if (StringUtils.isNotBlank(requestParams.getCity())){
            boolQueryBuilder.filter(QueryBuilders.termQuery("city",requestParams.getCity()));
        }
        //如果Brand不为空
        if (StringUtils.isNotBlank(requestParams.getBrand())){
            boolQueryBuilder.filter(QueryBuilders.termQuery("brand",requestParams.getBrand()));
        }
        //如果starName不为空
        if (StringUtils.isNotBlank(requestParams.getStarName())){
            boolQueryBuilder.filter(QueryBuilders.termQuery("starName",requestParams.getStarName()));
        }
        //最大值最小值不为空
        if (requestParams.getMaxPrice() !=null && requestParams.getMinPrice()!=null){
            boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(requestParams.getMinPrice()).lte(requestParams.getMaxPrice()));

        }
        //searchRequest.source().query(boolQueryBuilder);
        //算分控制
        FunctionScoreQueryBuilder functionScoreQuery =
                QueryBuilders.functionScoreQuery(
                        // 原始查询，相关性算分的查询
                        boolQueryBuilder,
                        // function score的数组
                        new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                                // 其中的一个function score 元素
                                new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                                        // 过滤条件
                                        QueryBuilders.termQuery("isAD", true),
                                        // 算分函数
                                        ScoreFunctionBuilders.weightFactorFunction(10)
                                )
                        });
        searchRequest.source().query(functionScoreQuery);
    }

    private PageResult handlerResponse(SearchResponse searchResponse) {

        List<HotelDoc> hotelDocs = new ArrayList<>();

        SearchHits hits = searchResponse.getHits();
        System.out.println("查询到的总条数" + hits.getTotalHits().value);
        SearchHit[] searchHits = hits.getHits();
        for (SearchHit hit : searchHits) {
            String sourceAsString = hit.getSourceAsString();
            //把JSON字符串反序列化成对象
            HotelDoc hotelDoc = JSON.parseObject(sourceAsString, HotelDoc.class);
            //处理高亮的结果
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            if (highlightFields != null) {
                HighlightField highlightField = highlightFields.get("name");
                if (highlightField != null) {
                    Text[] fragments = highlightField.fragments();
                    if (fragments != null && fragments.length > 0) {
                        String nameFighlightFields = fragments[0].toString();
                        hotelDoc.setName(nameFighlightFields);
                    }
                }
            }
            //显示距离
            Object[] sortValues = hit.getSortValues();
            if (sortValues !=null &&sortValues.length>0){
                hotelDoc.setDistance(sortValues[0]);
            }
            hotelDocs.add(hotelDoc);
        }
        PageResult result = new PageResult(hits.getTotalHits().value, hotelDocs);
        return result;
    }

}

